#include "src/aot-compiler/portable-serializer.h"
namespace portable {

// ========== implementation ==========
void PbcSerializer::Run() {
  OptimiseStringTable();
  for (uint32_t i = 0; i < func_list_.size(); ++i) {
    Function* func = func_list_[i];
    P_CHECK(i == func->func_id());
    uint32_t scope_id = GetOrCreateScope(*func->scope()).GetScopeId();
    P_CHECK(i == scope_id);
  }

  for (uint32_t i = 0; i < func_list_.size(); ++i) {
    SetCurrentFuncWriter(i);
    Function* func = func_list_[i];
    P_CHECK(func);
    P_CHECK(func->func_id() == i);
    uint32_t size_in_byte = SerializeFunction(*func, func->func_id());
    bytecode_storage_cursor_ += size_in_byte;
  }
  memset(&pbc_header_, 0, sizeof(pbc_header_));
  pbc_header_.magic_ = PBC_MAGIC;
  pbc_header_.version_ = PBC_VERSION;
  pbc_header_.top_level_func_id_ = 0;

  // TODO:(wwq) Add alignment
  pbc_header_.top_declarations_offset_ = sizeof(PbcHeader);
  pbc_header_.top_declarations_size_ = SerializeTopDeclarations();
  P_CHECK(top_declarations_.size() <= static_cast<size_t>(UINT32_MAX));
  pbc_header_.top_declarations_count_ =
      static_cast<uint32_t>(top_declarations_.size());

  pbc_header_.double_table_offset_ =
      pbc_header_.top_declarations_offset_ + pbc_header_.top_declarations_size_;
  pbc_header_.double_table_size_ = SerializeDoubleTable();
  P_CHECK(double_table_.size() <= static_cast<size_t>(UINT32_MAX));
  pbc_header_.double_count_ = static_cast<uint32_t>(double_table_.size());

  pbc_header_.string_storage_offset_ =
      pbc_header_.double_table_offset_ + pbc_header_.double_table_size_;
  pbc_header_.string_storage_size_ = SerializeStringStorage();
  P_CHECK(string_table_.size() <= static_cast<size_t>(UINT32_MAX));
  pbc_header_.string_count_ = static_cast<uint32_t>(string_table_.size());

  pbc_header_.u16string_storage_offset_ =
      pbc_header_.string_storage_offset_ + pbc_header_.string_storage_size_;
  pbc_header_.u16string_storage_size_ = SerializeU16StringStorage();
  P_CHECK(u16string_table_.size() <= static_cast<size_t>(UINT32_MAX));
  pbc_header_.u16string_count_ = static_cast<uint32_t>(u16string_table_.size());

  pbc_header_.bigint_storage_offset_ = pbc_header_.u16string_storage_offset_ +
                                       pbc_header_.u16string_storage_size_;
  pbc_header_.bigint_storage_size_ = SerializeBigIntStorage();
  P_CHECK(bigint_table_.size() <= static_cast<size_t>(UINT32_MAX));
  pbc_header_.bigint_count_ = static_cast<uint32_t>(bigint_table_.size());

  pbc_header_.literal_storage_offset_ =
      pbc_header_.bigint_storage_offset_ + pbc_header_.bigint_storage_size_;
  pbc_header_.literal_storage_size_ = SerializeLiteralStorage();
  P_CHECK(literal_table_.size() <= static_cast<size_t>(UINT32_MAX));
  pbc_header_.literal_count_ = static_cast<uint32_t>(literal_table_.size());

  pbc_header_.scope_storage_offset_ =
      pbc_header_.literal_storage_offset_ + pbc_header_.literal_storage_size_;
  pbc_header_.scope_storage_size_ = SerializeScopeStorage();
  P_CHECK(scope_table_.size() <= static_cast<size_t>(UINT32_MAX));
  pbc_header_.scope_count_ = static_cast<uint32_t>(scope_table_.size());

  pbc_header_.function_table_offset_ =
      pbc_header_.scope_storage_offset_ + pbc_header_.scope_storage_size_;
  pbc_header_.function_table_size_ = SerializeFunctionHeaderTable();
  P_CHECK(func_list_.size() <= static_cast<size_t>(UINT32_MAX));
  pbc_header_.function_count_ = static_cast<uint32_t>(func_list_.size());

  pbc_header_.bytecode_storage_offset_ =
      pbc_header_.function_table_offset_ + pbc_header_.function_table_size_;
}
#if 0
void PbcSerializer::Output(std::ofstream& os) {
  // step1 pbc header, note there is no valid hash value here
  os.write(reinterpret_cast<const char*>(&pbc_header_), sizeof(pbc_header_));

  // step2 all kinds of table and storage
  const std::vector<uint8_t> script_buffer = script_writer_->Buffer();
  os.write(reinterpret_cast<const char*>(script_buffer.data()),
           script_buffer.size());

  // step3 bytecode storage
  for (size_t i = 0; i < func_writers_.size(); ++i) {
    P_CHECK(func_writers_[i]);
    const std::vector<uint8_t> func_buffer = func_writers_[i]->Buffer();
    os.write(reinterpret_cast<const char*>(func_buffer.data()),
             func_buffer.size());
  }
}
#endif

void PbcSerializer::Output(std::vector<uint8_t>& buffer) {
  const std::vector<uint8_t> script_buffer = script_writer_->Buffer();
  const size_t pbc_header_size = sizeof(pbc_header_);
  size_t total_size = pbc_header_size + script_buffer.size();
  for (size_t i = 0; i < func_writers_.size(); ++i) {
    P_CHECK(func_writers_[i]);
    const std::vector<uint8_t> func_buffer = func_writers_[i]->Buffer();
    total_size += func_buffer.size();
  }
  buffer.resize(total_size);

  uint8_t* addr = buffer.data();
  // step1 skip pbc header
  addr += pbc_header_size;

  // step2 all kinds of table and storage
  memcpy(addr, reinterpret_cast<const char*>(script_buffer.data()),
         script_buffer.size());
  addr += script_buffer.size();

  // step3 bytecode storage
  for (size_t i = 0; i < func_writers_.size(); ++i) {
    P_CHECK(func_writers_[i]);
    const std::vector<uint8_t> func_buffer = func_writers_[i]->Buffer();
    memcpy(addr, reinterpret_cast<const char*>(func_buffer.data()),
           func_buffer.size());
    addr += func_buffer.size();
  }

  // step4 set hash value and pbc header
  pbc_header_.pbc_hash_ = v8::base::hash_range(buffer.data() + pbc_header_size,
                                               buffer.data() + total_size);
  memcpy(buffer.data(), reinterpret_cast<const char*>(&pbc_header_),
         pbc_header_size);
}

bool PbcSerializer::CheckToPbcShortValue(const LiteralElement& elem) {
  switch (elem.get_tag()) {
    case LiteralElement::Tag::kNull:
    case LiteralElement::Tag::kUndefined:
    case LiteralElement::Tag::kUninitialized:
    case LiteralElement::Tag::kTheHole:
    case LiteralElement::Tag::kTrue:
    case LiteralElement::Tag::kFalse: {
      return true;
    }
    case LiteralElement::Tag::kSmi: {
      return static_cast<uint32_t>(Smi::ToInt(elem.smi())) <=
             PbcShortSmi::ValueBits::kMax;
    }
    case LiteralElement::Tag::kString: {
      StringRef ref = GetOrCreateString(elem.string());
      return (ref.GetLength() <= PbcShortStringRef::LengthBits::kMax) &&
             (ref.GetOffset() <= PbcShortStringRef::OffsetBits::kMax);
    }
    case LiteralElement::Tag::kArray: {
      LiteralRef ref = GetOrCreateArrayLiteral(elem);
      return ref.GetOffset() <= PbcShortLiteralRef::OffsetBits::kMax;
    }
    case LiteralElement::Tag::kObject: {
      LiteralRef ref = GetOrCreateObjectLiteral(elem);
      return ref.Kind() == PbcValueKind::kDefaultObjectLiteral &&
             ref.GetOffset() <= PbcShortLiteralRef::OffsetBits::kMax;
    }
    default:
      return false;
  }
}

bool PbcSerializer::CheckToShortConstantTable(
    const ConstantPool& constant_pool) {
  for (uint32_t i = 0; i < constant_pool.length(); ++i) {
    const ConstantArrayBuilder::Entry& entry = constant_pool.Get(i);
    switch (entry.tag_) {
      case ConstantArrayBuilder::Entry::Tag::kTopDeclarations:
      case ConstantArrayBuilder::Entry::Tag::kNaN:
      case ConstantArrayBuilder::Entry::Tag::kTheHoleValue:
      case ConstantArrayBuilder::Entry::Tag::kClassFieldsSymbol:
      case ConstantArrayBuilder::Entry::Tag::
          kEmptyObjectBoilerplateDescription: {
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kSmi: {
        if (entry.GetSmi() > PbcShortSmi::ValueBits::kMax) {
          return false;
        }
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kJumpTableSmi: {
        if (entry.GetJumpTableSmi() > PbcShortSmi::ValueBits::kMax) {
          return false;
        }
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kU8String:
      case ConstantArrayBuilder::Entry::Tag::kU16String: {
        StringRef item = GetOrCreateString(entry.str_);
        if ((item.GetLength() > PbcShortStringRef::LengthBits::kMax) ||
            (item.GetOffset() > PbcShortStringRef::OffsetBits::kMax)) {
          return false;
        }
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kFunction: {
        auto* func = reinterpret_cast<Function*>(entry.ref_);
        FunctionRef item(
            func->func_id(),
            func->internal_formal_parameter_count_without_receiver());
        if (item.GetParameterCount() >
                PbcShortFunction::ParameterCountBits::kMax ||
            item.GetFuncId() > PbcShortFunction::FuncIdBits::kMax) {
          return false;
        }
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kScope: {
        auto* scope = reinterpret_cast<Scope*>(entry.ref_);
        ScopeRef item = GetOrCreateScope(*scope);
        if (item.GetScopeId() > PbcShortScope::ScopeIdBits::kMax) {
          return false;
        }
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kArrayLiteral:
      case ConstantArrayBuilder::Entry::Tag::kObjectLiteral: {
        LiteralElement* elem = reinterpret_cast<LiteralElement*>(entry.ref_);
        if (!CheckToPbcShortValue(*elem)) {
          return false;
        }
        break;
      }
      default:
        return false;
    }
  }
  return true;
}

PbcShortValue PbcSerializer::ToPbcShortValue(const LiteralElement& elem) {
  P_DCHECK(CheckToPbcShortValue(elem));
  switch (elem.get_tag()) {
    case LiteralElement::Tag::kNull: {
      return PbcShortUnique(UniqueTag::kTagNull);
    }
    case LiteralElement::Tag::kUndefined: {
      return PbcShortUnique(UniqueTag::kTagUndefined);
    }
    case LiteralElement::Tag::kUninitialized: {
      return PbcShortUnique(UniqueTag::kTagUninitialized);
    }
    case LiteralElement::Tag::kTheHole: {
      return PbcShortUnique(UniqueTag::kTagTheHole);
    }
    case LiteralElement::Tag::kTrue: {
      return PbcShortUnique(UniqueTag::kTagTrue);
    }
    case LiteralElement::Tag::kFalse: {
      return PbcShortUnique(UniqueTag::kTagFalse);
    }
    case LiteralElement::Tag::kSmi: {
      return PbcShortSmi(static_cast<uint32_t>(Smi::ToInt(elem.smi())));
    }
    case LiteralElement::Tag::kString: {
      StringRef ref = GetOrCreateString(elem.string());
      return PbcShortStringRef(ref.Kind() == PbcValueKind::kU8String,
                               ref.GetOffset(), ref.GetLength());
    }
    case LiteralElement::Tag::kArray: {
      LiteralRef item = GetOrCreateArrayLiteral(elem);
      return PbcShortLiteralRef(PbcShortValueKind::kShortArrayLiteral,
                                item.GetOffset());
    }
    case LiteralElement::Tag::kObject: {
      LiteralRef item = GetOrCreateObjectLiteral(elem);
      return PbcShortLiteralRef(PbcShortValueKind::kShortObjectLiteral,
                                item.GetOffset());
    }
    default:
      P_UNREACHABLE();
  }
}

PbcValue PbcSerializer::ToPbcValue(const LiteralElement& elem) {
  switch (elem.get_tag()) {
    case LiteralElement::Tag::kSmi: {
      return SmiScalar(Smi::ToInt(elem.smi()));
    }
    case LiteralElement::Tag::kNumber: {
      return GetOrCreateDouble(elem.number());
    }
    case LiteralElement::Tag::kNull: {
      return UniqueScalar::Null();
    }
    case LiteralElement::Tag::kUndefined: {
      return UniqueScalar::Undefined();
    }
    case LiteralElement::Tag::kUninitialized: {
      return UniqueScalar::Uninitialized();
    }
    case LiteralElement::Tag::kTheHole: {
      return UniqueScalar::TheHole();
    }
    case LiteralElement::Tag::kTrue: {
      return UniqueScalar::True();
    }
    case LiteralElement::Tag::kFalse: {
      return UniqueScalar::False();
    }
    case LiteralElement::Tag::kString: {
      return GetOrCreateString(elem.string());
    }
    case LiteralElement::Tag::kBigInt: {
      return GetOrCreateBigInt(elem.bigint());
    }
    case LiteralElement::Tag::kArray: {
      return GetOrCreateArrayLiteral(elem);
    }
    case LiteralElement::Tag::kObject: {
      return GetOrCreateObjectLiteral(elem);
    }
    default: {
      // TODO:(wwq) Support more tag
      P_CHECK(false);
      break;
    }
  }
  P_UNREACHABLE();
}

template <typename T>
void PbcSerializer::OptimiseSubstrInStringTable(std::vector<T>& strings) {
  // Optimise with Aho–Corasick algorithm.
  std::sort(strings.begin(), strings.end(), [](const T& a, const T& b) {
    return a.length() != b.length() ? a.length() > b.length() : a < b;
  });
  strings.resize(std::unique(strings.begin(), strings.end()) - strings.begin());

  T string_pool;
  for (uint32_t i = 0; i < strings.size(); ++i) {
    const T& pattern = strings[i];
    size_t offset = string_pool.find(pattern);

    if (offset == std::string::npos) {
      if constexpr (std::is_same_v<T, std::string>) {
        string_table_.push_back(pattern);
      } else if constexpr (std::is_same_v<T, std::u16string>) {
        u16string_table_.push_back(pattern);
      }
      offset = string_pool.size();
      string_pool += pattern;
    }
    if constexpr (std::is_same_v<T, std::string>) {
      StringRef item(PbcValueKind::kU8String, static_cast<uint32_t>(offset),
                     static_cast<uint32_t>(pattern.size()));
      string_map_.emplace(pattern, item);
    } else if constexpr (std::is_same_v<T, std::u16string>) {
      StringRef item(PbcValueKind::kU16String,
                     static_cast<uint32_t>(offset << 1),
                     static_cast<uint32_t>(pattern.size()));
      u16string_map_.emplace(pattern, item);
    }
  }

  if constexpr (std::is_same_v<T, std::string>) {
    string_cursor_ = static_cast<uint32_t>(string_pool.size());
  } else if constexpr (std::is_same_v<T, std::u16string>) {
    u16string_cursor_ = static_cast<uint32_t>(string_pool.size());
  }
}

void PbcSerializer::OptimiseStringTable() {
  std::vector<std::string> u8strings;
  std::vector<std::u16string> u16strings;
  auto insert = [&u8strings, &u16strings](const String& s) {
    if (std::holds_alternative<std::string>(s)) {
      u8strings.push_back(std::get<std::string>(s));
    } else {
      u16strings.push_back(std::get<std::u16string>(s));
    }
  };
  auto insertFromScope = [&insert](const Scope* scope) {
    const std::vector<String>& locals = scope->GetLocals();
    uint32_t locals_size = static_cast<uint32_t>(locals.size());
    for (uint32_t i = 0; i < locals_size; ++i) {
      insert(locals[i]);
    }
  };
  auto insertFromElementImpl = [&insert](auto&& self,
                                         const LiteralElement* elem) -> void {
    switch (elem->get_tag()) {
      case LiteralElement::Tag::kString: {
        insert(elem->string());
        break;
      }
      case LiteralElement::Tag::kArray:
      case LiteralElement::Tag::kObject: {
        const auto& elements = elem->get_elements();
        for (size_t i = 0; i < elements.size(); ++i) {
          self(self, &elements[i]);
        }
        break;
      }
      case LiteralElement::Tag::kClass: {
        const ClassDescription* desc = elem->get_class_desc();
        for (size_t i = 0; i < desc->prop_.size(); ++i) {
          self(self, &desc->prop_[i].key_);
        }
        break;
      }
      default:
        break;
    }
  };
  auto insertFromElement =
      [&insertFromElementImpl](const LiteralElement* elem) {
        insertFromElementImpl(insertFromElementImpl, elem);
      };

  // Collect string
  for (uint32_t i = 0; i < func_list_.size(); ++i) {
    Function* func = func_list_[i];
    insert(func->function_name());
    insertFromScope(func->scope());

    ConstantPool* constant_pool = func->bytecode_array()->constant_pool();
    for (uint32_t idx = 0; idx < constant_pool->length(); ++idx) {
      const ConstantArrayBuilder::Entry& entry = constant_pool->Get(idx);
      switch (entry.tag_) {
        case ConstantArrayBuilder::Entry::Tag::kU8String:
        case ConstantArrayBuilder::Entry::Tag::kU16String: {
          insert(entry.str_);
          break;
        }
        case ConstantArrayBuilder::Entry::Tag::kArrayLiteral:
        case ConstantArrayBuilder::Entry::Tag::kObjectLiteral:
        case ConstantArrayBuilder::Entry::Tag::kClassLiteral: {
          insertFromElement(reinterpret_cast<LiteralElement*>(entry.ref_));
          break;
        }
        case ConstantArrayBuilder::Entry::Tag::kTopDeclarations: {
          const std::vector<String>& declarations =
              *reinterpret_cast<std::vector<String>*>(entry.ref_);
          for (const String& str : declarations) {
            insert(str);
          }
          break;
        }
        case ConstantArrayBuilder::Entry::Tag::kScope: {
          Scope* scope = reinterpret_cast<Scope*>(entry.ref_);
          switch (scope->GetScopeType()) {
            case ScopeType::kScriptScope:
            case ScopeType::kFunctionScope:
              break;
            default:
              insertFromScope(scope);
              break;
          }
          break;
        }
        case ConstantArrayBuilder::Entry::Tag::kTemplateObjectLiteral: {
          TemplateObjectDescription* object =
              reinterpret_cast<TemplateObjectDescription*>(entry.ref_);
          const auto& raws = object->GetRaws();
          const auto& cookeds = object->GetCookeds();
          for (uint32_t i = 0; i < raws.size(); ++i) {
            insertFromElement(&raws[i]);
          }
          if (!object->IsDup()) {
            for (size_t i = 0; i < cookeds.size(); ++i) {
              insertFromElement(&cookeds[i]);
            }
          }
          break;
        }
        default: {
          break;
        }
      }
    }
  }

  OptimiseSubstrInStringTable(u8strings);
  OptimiseSubstrInStringTable(u16strings);
}

LiteralRef PbcSerializer::GetOrCreateArrayLiteral(const LiteralElement& array) {
  P_CHECK(array.get_tag() == LiteralElement::Tag::kArray);

  const auto& elements = array.get_elements();
  auto it = array_literal_map_.begin();
  while (!array_literal_map_.empty() && it != array_literal_map_.end()) {
    if (*it == elements) {
      return array_literal_ref_[std::distance(array_literal_map_.begin(), it)];
    }
    it++;
  }

  bool is_short_value = true;
  for (uint32_t i = 0; i < elements.size(); ++i) {
    if (!CheckToPbcShortValue(elements[i])) {
      is_short_value = false;
      break;
    }
  }

  size_t value_size = is_short_value ? PbcShortValue::kSize : PbcValue::kSize;

  P_CHECK(sizeof(RawArrayLiteral) + value_size * elements.size() <=
          static_cast<size_t>(UINT32_MAX));
  uint32_t elements_size = static_cast<uint32_t>(elements.size());
  uint32_t size_in_byte = sizeof(RawArrayLiteral) + value_size * elements_size;
  RawArrayLiteral* array_literal =
      reinterpret_cast<RawArrayLiteral*>(malloc(size_in_byte));
  memset(array_literal, 0, size_in_byte);
  array_literal->is_short_value_ = is_short_value;
  DCHECK(!(is_short_value && i::IsDoubleElementsKind(
                                 static_cast<i::ElementsKind>(array.kind()))));
  array_literal->length_ = elements_size;
  array_literal->flags_.is_cow_ = array.is_cow();
  array_literal->flags_.kind_ = array.kind();
  for (uint32_t i = 0; i < elements_size; ++i) {
    const LiteralElement& elem = elements[i];
    if (is_short_value) {
      PbcShortValue value = ToPbcShortValue(elem);
      array_literal->short_elements_[i] = value;
    } else {
      PbcValue value = ToPbcValue(elem);
      array_literal->elements_[i] = value;
    }
  }
  LiteralRef literal_ref(PbcValueKind::kArrayLiteral, literal_cursor_);
  literal_cursor_ += size_in_byte;
  literal_table_.emplace_back(reinterpret_cast<uint8_t*>(array_literal),
                              size_in_byte);
  array_literal_map_.emplace_back(elements);
  array_literal_ref_.emplace_back(literal_ref);
  return literal_ref;
}

LiteralRef PbcSerializer::GetOrCreateObjectLiteral(
    const LiteralElement& object) {
  P_CHECK(object.get_tag() == LiteralElement::Tag::kObject);

  const auto& elements = object.get_elements();
  auto it = object_literal_map_.begin();
  while (!object_literal_map_.empty() && it != object_literal_map_.end()) {
    if (*it == elements) {
      return object_literal_ref_[std::distance(object_literal_map_.begin(),
                                               it)];
    }
    it++;
  }

  LiteralElement::ObjectDescript desc = object.get_obj_desc();
  uint32_t elements_size = static_cast<uint32_t>(elements.size());
  P_CHECK(elements_size % 2 == 0);
  if (desc.IsDefaultObjectDescript(elements_size / 2)) {
    bool is_short_value = true;
    for (uint32_t i = 0; i < elements_size; ++i) {
      if (!CheckToPbcShortValue(elements[i])) {
        is_short_value = false;
        break;
      }
    }

    size_t value_size = is_short_value ? PbcShortValue::kSize : PbcValue::kSize;
    P_CHECK(sizeof(RawDefaultObjectLiteral) + value_size * elements.size() <=
            static_cast<size_t>(UINT32_MAX));
    uint32_t size_in_byte =
        sizeof(RawDefaultObjectLiteral) + value_size * elements_size;
    RawDefaultObjectLiteral* object_literal =
        reinterpret_cast<RawDefaultObjectLiteral*>(malloc(size_in_byte));
    memset(object_literal, 0, size_in_byte);
    object_literal->number_of_properties_ = elements_size / 2;
    object_literal->is_short_value_ = is_short_value;
    for (uint32_t i = 0; i < elements_size; i += 2) {
      const LiteralElement& key_elem = elements[i];
      const LiteralElement& value_elem = elements[i + 1];
      uint32_t property_index = i / 2;
      if (is_short_value) {
        object_literal->short_properties_[property_index].key_ =
            ToPbcShortValue(key_elem);
        object_literal->short_properties_[property_index].value_ =
            ToPbcShortValue(value_elem);
      } else {
        object_literal->properties_[property_index].key_ = ToPbcValue(key_elem);
        object_literal->properties_[property_index].value_ =
            ToPbcValue(value_elem);
      }
    }

    LiteralRef literal_ref(PbcValueKind::kDefaultObjectLiteral,
                           literal_cursor_);
    literal_cursor_ += size_in_byte;
    literal_table_.emplace_back(reinterpret_cast<uint8_t*>(object_literal),
                                size_in_byte);
    object_literal_map_.emplace_back(elements);
    object_literal_ref_.emplace_back(literal_ref);
    return literal_ref;
  }

  P_CHECK(sizeof(RawObjectLiteral) + PbcValue::kSize * elements.size() <=
          static_cast<size_t>(UINT32_MAX));
  uint32_t size_in_byte =
      sizeof(RawObjectLiteral) + PbcValue::kSize * elements_size;
  RawObjectLiteral* object_literal =
      reinterpret_cast<RawObjectLiteral*>(malloc(size_in_byte));
  memset(object_literal, 0, size_in_byte);
  object_literal->number_of_properties_ = elements_size / 2;
  object_literal->desc_.boilerplate_description_flag_ =
      desc.boilerplate_description_flag_;
  object_literal->desc_.boilerplate_properties_ = desc.boilerplate_properties_;
  object_literal->desc_.property_length_ = desc.property_length_;
  object_literal->desc_.index_keys_ = desc.index_keys_;
  object_literal->desc_.has_seen_proto_ = desc.has_seen_proto_;
  for (uint32_t i = 0; i < elements_size; i += 2) {
    const LiteralElement& key_elem = elements[i];
    const LiteralElement& value_elem = elements[i + 1];
    uint32_t property_index = i / 2;
    object_literal->properties_[property_index].key_ = ToPbcValue(key_elem);
    object_literal->properties_[property_index].value_ = ToPbcValue(value_elem);
  }
  LiteralRef literal_ref(PbcValueKind::kObjectLiteral, literal_cursor_);
  literal_cursor_ += size_in_byte;
  literal_table_.emplace_back(reinterpret_cast<uint8_t*>(object_literal),
                              size_in_byte);
  object_literal_map_.emplace_back(elements);
  object_literal_ref_.emplace_back(literal_ref);
  return literal_ref;
}

LiteralRef PbcSerializer::GetOrCreateTemplateObjectLiteral(
    const TemplateObjectDescription& object) {
  const auto& raws = object.GetRaws();
  const auto& cookeds = object.GetCookeds();
  bool isDup = object.IsDup();
  P_CHECK(sizeof(RawTemplateLiteral) + PbcValue::kSize * raws.size() <=
          static_cast<size_t>(UINT32_MAX));
  uint32_t raws_size = static_cast<uint32_t>(raws.size());
  uint32_t size_in_byte =
      sizeof(RawTemplateLiteral) +
      PbcValue::kSize * raws_size /*sizeof(StringRef) == PbcValue::kSize*/;
  if (!isDup) {
    P_CHECK(size_in_byte += PbcValue::kSize * cookeds.size() <=
                            static_cast<size_t>(UINT32_MAX));
    size_in_byte += PbcValue::kSize * static_cast<uint32_t>(cookeds.size());
  }
  RawTemplateLiteral* template_literal =
      reinterpret_cast<RawTemplateLiteral*>(malloc(size_in_byte));
  memset(template_literal, 0, size_in_byte);
  template_literal->flags_ = {isDup};
  P_CHECK(isDup || raws.size() == cookeds.size());
  template_literal->length_ = raws_size;

  for (uint32_t i = 0; i < raws_size; ++i) {
    template_literal->raw_and_cookeds_[i] = ToPbcValue(raws[i]);
  }
  if (!isDup) {
    for (size_t i = 0; i < cookeds.size(); ++i) {
      template_literal->raw_and_cookeds_[raws_size + i] =
          ToPbcValue(cookeds[i]);
    }
  }

  LiteralRef literal_ref(PbcValueKind::kTemplateLiteral, literal_cursor_);
  literal_cursor_ += size_in_byte;
  literal_table_.emplace_back(reinterpret_cast<uint8_t*>(template_literal),
                              size_in_byte);
  return literal_ref;
}

LiteralRef PbcSerializer::GetOrCreateClassLiteral(
    const LiteralElement& literal) {
  CHECK(literal.get_tag() == LiteralElement::Tag::kClass);
  const ClassDescription* desc = literal.get_class_desc();

  P_CHECK(sizeof(RawClassLiteral) +
              sizeof(ClassProperty) * desc->prop_.size() <=
          static_cast<size_t>(UINT32_MAX));
  uint32_t count = static_cast<uint32_t>(desc->prop_.size());
  uint32_t size_in_byte = static_cast<uint32_t>(sizeof(RawClassLiteral) +
                                                sizeof(ClassProperty) * count);
  RawClassLiteral* class_literal =
      reinterpret_cast<RawClassLiteral*>(malloc(size_in_byte));
  memset(class_literal, 0, size_in_byte);
  class_literal->start_ = desc->start_;
  class_literal->end_ = desc->end_;
  class_literal->number_of_properties_ = desc->prop_.size();
  for (uint32_t i = 0; i < count; ++i) {
    auto prop = desc->prop_[i];
    class_literal->properties_[i].flags_.is_static_ = prop.is_static_;
    class_literal->properties_[i].flags_.is_private_ = prop.is_private_;
    class_literal->properties_[i].flags_.is_computed_name_ =
        prop.is_computed_name_;
    class_literal->properties_[i].flags_.kind_ = prop.kind_;
    class_literal->properties_[i].key_ = ToPbcValue(prop.key_);
  }

  LiteralRef literal_ref(PbcValueKind::kClassLiteral, literal_cursor_);
  literal_cursor_ += size_in_byte;
  literal_table_.emplace_back(reinterpret_cast<uint8_t*>(class_literal),
                              size_in_byte);
  return literal_ref;
}

ScopeRef PbcSerializer::GetCreatedScope(const Scope& scope) {
  auto it = scope_map_.find(&scope);
  P_CHECK(it != scope_map_.end());
  ScopeRef scope_ref = it->second.second;
  P_CHECK(scope_ref.IsValid());
  return scope_ref;
}

ScopeRef PbcSerializer::GetOrCreateScope(const Scope& scope) {
  auto it = scope_map_.find(&scope);
  if (it != scope_map_.end()) {
    ScopeRef scope_ref = it->second.second;
    P_CHECK(scope_ref.IsValid());
    return scope_ref;
  }

  const std::vector<String>& locals = scope.GetLocals();
  P_CHECK(sizeof(RawScope) + sizeof(LocalVar) * locals.size() <=
          static_cast<size_t>(UINT32_MAX));
  const uint32_t locals_size = static_cast<uint32_t>(locals.size());

  // 1.RawScope
  // uint32_t size_in_byte = sizeof(RawScope);  //+ sizeof(LocalVar) *
  // locals_size;
  RawScope* raw_scope = reinterpret_cast<RawScope*>(malloc(sizeof(RawScope)));
  memset(raw_scope, 0, sizeof(RawScope));
  ScopeFlags scope_flag = ScopeFlags();
  scope_flag.scope_type_ = scope.GetScopeType();
  raw_scope->flags_ = *reinterpret_cast<uint8_t*>(&scope_flag);
  raw_scope->first_local_index_ = local_cursor_;
  raw_scope->num_local_vars_ = locals_size;
  raw_scope->function_id_ = scope.func_id();
  // outer scope should have been created.
  if (scope.GetOuterScope() == nullptr) {
    raw_scope->outer_scope_id_ = ScopeRef::Invalid().GetScopeId();
  } else {
    raw_scope->outer_scope_id_ =
        GetCreatedScope(*scope.GetOuterScope()).GetScopeId();
  }

  // 2.LocalVars
  std::vector<uint8_t> scope_locals(locals_size * sizeof(LocalVar), 0);
  for (uint32_t i = 0; i < locals_size; ++i) {
    const String& str = locals[i];
    StringRef ref;
    if (std::holds_alternative<std::string>(str)) {
      ref = GetOrCreateU8String(std::get<std::string>(str));
    } else {
      ref = GetOrCreateU16String(std::get<std::u16string>(str));
    }

    LocalVar& local_var = (reinterpret_cast<LocalVar*>(scope_locals.data()))[i];
    local_var.is_u8stirng = ref.Kind() == PbcValueKind::kU8String;
    P_CHECK(ref.GetLength() < (1 << VAR_NAME_BIT_LIMIT));
    local_var.offset_ = ref.GetOffset();
    local_var.length_ = ref.GetLength();
    // TODO:(wwq) set others field
    local_var.flags_ = 0;
  }

  // TODO:(wwq) set outer scope, inner scope...
  ScopeRef scope_ref(static_cast<uint32_t>(scope_table_.size()));
  local_var_table_.emplace_back(scope_locals);
  scope_table_.push_back(raw_scope);
  local_cursor_ += locals_size;

  // scope_cursor_ += size_in_byte;
  // scope_table_.emplace_back(reinterpret_cast<uint8_t*>(raw_scope),
  //                           size_in_byte);
  scope_map_.emplace(&scope, std::pair{raw_scope, scope_ref});
  return scope_ref;
}

uint32_t PbcSerializer::SerializeFunctionHeaderTable() {
  P_CHECK(sizeof(FunctionHeader) * func_headers_.size() <=
          static_cast<size_t>(UINT32_MAX));
  uint32_t size_in_byte =
      static_cast<uint32_t>(sizeof(FunctionHeader) * func_headers_.size());
  script_writer_->WriteBytes(
      reinterpret_cast<const uint8_t*>(func_headers_.data()), size_in_byte);
  return size_in_byte;
}

uint32_t PbcSerializer::SerializeFunction(Function& func, uint32_t func_id) {
  FunctionHeader& header = func_headers_[func_id];
  memset(&header, 0, sizeof(header));
  SetCurrentFuncWriter(func_id);

  StringRef name = GetOrCreateString(func.function_name());
  header.is_u8string_ = name.Kind() == PbcValueKind::kU8String;
  header.name_offset_ = name.GetOffset();
  header.name_length_ = name.GetLength();
  DCHECK(func_id == GetOrCreateScope(*func.scope()).GetScopeId());

  // TODO:(wwq) set function flags
  header.flags_.is_copyable_ = func.copyable();
  header.flags_.kind_ = static_cast<PbcFunctionKind>(func.kind());
  header.flags_.syntax_kind_ =
      static_cast<PbcFunctionSyntaxKind>(func.syntax_kind());
  header.flags_.is_strict_mode_ =
      (func.language_mode() == LanguageMode::kStrict);
  header.flags_.is_null_name_ = func.is_null_name();
  header.flags_.requires_instance_members_initializer_ =
      func.requires_instance_members_initializer();
  header.expected_nof_properties_ = func.expected_nof_properties();

  const BytecodeArray& bytecode = *func.bytecode_array();
  header.register_count_ = bytecode.register_count();
  header.parameter_count_ = bytecode.parameter_count();
  header.length_ = func.length();
  header.suspend_count_ = func.suspend_count();
  header.exception_count_ = bytecode.exception_count();
  header.incoming_new_target_or_generator_register_ =
      (1 << INCOMINT_NEW_TARGET_OR_GENERATOR_REGISTER_BIT) - 1;
  if (bytecode.incoming_new_target_or_generator_register().is_valid()) {
    DCHECK(bytecode.incoming_new_target_or_generator_register().index() <
           INCOMINT_NEW_TARGET_OR_GENERATOR_REGISTER_BIT);
    header.incoming_new_target_or_generator_register_ =
        bytecode.incoming_new_target_or_generator_register().index();
  }

  uint32_t pos_before = current_func_writer_->Position();
  header.bytecode_offset_ = bytecode_storage_cursor_;
  uint32_t bytecode_size = SerializeBytecode(bytecode);
  header.constant_pool_offset_ = header.bytecode_offset_ + bytecode_size;

  const ConstantPool& const_pool = *func.bytecode_array()->constant_pool();
  if (CheckToShortConstantTable(const_pool)) {
    SerializeShortConstantTable(const_pool);
  } else {
    SerializeConstantTable(const_pool);
  }

  uint32_t pos_after = current_func_writer_->Position();
  return pos_after - pos_before;
}

// See RawBytecode
uint32_t PbcSerializer::SerializeBytecode(const BytecodeArray& bytecode) {
  P_CHECK(static_cast<size_t>(bytecode.length()) ==
          bytecode.raw_bytecodes_data().size());
  uint32_t pos_before = current_func_writer_->Position();
  P_CHECK(bytecode.raw_bytecodes_data().size() <=
          static_cast<size_t>(UINT32_MAX));
  uint32_t length = static_cast<uint32_t>(bytecode.raw_bytecodes_data().size());
  current_func_writer_->WriteInt32(length);
  current_func_writer_->WriteBytes(bytecode.raw_bytecodes_data().data(),
                                   length);
  uint32_t pos_after = current_func_writer_->Position();
  return pos_after - pos_before;
}

uint32_t PbcSerializer::SerializeShortConstantTable(
    const ConstantPool& constant_pool) {
  uint32_t pos_before = current_func_writer_->Position();
  current_func_writer_->WriteByte(true);
  current_func_writer_->WritePaddings(3);
  current_func_writer_->WriteInt32(constant_pool.length());

  for (uint32_t i = 0; i < constant_pool.length(); ++i) {
    const ConstantArrayBuilder::Entry& entry = constant_pool.Get(i);
    switch (entry.tag_) {
      case ConstantArrayBuilder::Entry::Tag::kTopDeclarations: {
        const std::vector<String>& declarations =
            *reinterpret_cast<std::vector<String>*>(entry.ref_);
        top_declarations_.reserve(declarations.size());
        for (const String& str : declarations) {
          StringRef item = GetOrCreateString(str);
          top_declarations_.push_back(item);
        }
        current_func_writer_->WriteShortConstantEntry(
            PbcShortUnique(UniqueTag::kTagTopDeclarations));
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kNaN: {
        current_func_writer_->WriteShortConstantEntry(
            PbcShortUnique(UniqueTag::kTagNaN));
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kTheHoleValue: {
        current_func_writer_->WriteShortConstantEntry(
            PbcShortUnique(UniqueTag::kTagTheHole));
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kClassFieldsSymbol: {
        current_func_writer_->WriteShortConstantEntry(
            PbcShortUnique(UniqueTag::kClassFieldsSymbol));
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::
          kEmptyObjectBoilerplateDescription: {
        current_func_writer_->WriteShortConstantEntry(
            PbcShortUnique(UniqueTag::kEmptyObjectBoilerplateDescription));
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kSmi: {
        current_func_writer_->WriteShortConstantEntry(
            PbcShortSmi(static_cast<uint32_t>(entry.GetSmi())));
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kJumpTableSmi: {
        current_func_writer_->WriteShortConstantEntry(
            PbcShortSmi(static_cast<uint32_t>(entry.GetJumpTableSmi())));
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kU8String:
      case ConstantArrayBuilder::Entry::Tag::kU16String: {
        StringRef ref = GetOrCreateString(entry.str_);
        PbcShortStringRef item(ref.Kind() == PbcValueKind::kU8String,
                               ref.GetOffset(), ref.GetLength());
        current_func_writer_->WriteShortConstantEntry(item);
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kFunction: {
        auto* func = reinterpret_cast<Function*>(entry.ref_);
        PbcShortFunction item(
            func->func_id(),
            func->internal_formal_parameter_count_without_receiver());
        current_func_writer_->WriteShortConstantEntry(item);
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kScope: {
        auto* scope = reinterpret_cast<Scope*>(entry.ref_);
        ScopeRef ref = GetOrCreateScope(*scope);
        PbcShortScope item(ref.GetScopeId());
        current_func_writer_->WriteShortConstantEntry(item);
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kArrayLiteral:
      case ConstantArrayBuilder::Entry::Tag::kObjectLiteral: {
        LiteralElement* elem = reinterpret_cast<LiteralElement*>(entry.ref_);
        current_func_writer_->WriteShortConstantEntry(ToPbcShortValue(*elem));
        break;
      }
      default:
        P_DCHECK(false);
    }
  }
  uint32_t pos_after = current_func_writer_->Position();
  return pos_after - pos_before;
}

// See RawConstantTable
// return serialized byte size
uint32_t PbcSerializer::SerializeConstantTable(
    const ConstantPool& constant_pool) {
  uint32_t pos_before = current_func_writer_->Position();
  current_func_writer_->WritePaddings(4);
  current_func_writer_->WriteInt32(constant_pool.length());
  for (uint32_t i = 0; i < constant_pool.length(); ++i) {
    const ConstantArrayBuilder::Entry& entry = constant_pool.Get(i);
    switch (entry.tag_) {
      case ConstantArrayBuilder::Entry::Tag::kSmi: {
        SmiScalar item(entry.GetSmi());
        current_func_writer_->WriteConstantEntry(item);
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kArrayLiteral: {
        LiteralElement* array = reinterpret_cast<LiteralElement*>(entry.ref_);
        LiteralRef item = GetOrCreateArrayLiteral(*array);
        current_func_writer_->WriteConstantEntry(item);
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kObjectLiteral: {
        LiteralElement* object = reinterpret_cast<LiteralElement*>(entry.ref_);
        LiteralRef item = GetOrCreateObjectLiteral(*object);
        current_func_writer_->WriteConstantEntry(item);
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kTemplateObjectLiteral: {
        TemplateObjectDescription* object =
            reinterpret_cast<TemplateObjectDescription*>(entry.ref_);
        LiteralRef item = GetOrCreateTemplateObjectLiteral(*object);
        current_func_writer_->WriteConstantEntry(item);
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kClassLiteral: {
        LiteralElement* classLiteral =
            reinterpret_cast<LiteralElement*>(entry.ref_);
        LiteralRef item = GetOrCreateClassLiteral(*classLiteral);
        current_func_writer_->WriteConstantEntry(item);
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kNumber: {
        DoubleRef item = GetOrCreateDouble(entry.GetDouble());
        current_func_writer_->WriteConstantEntry(item);
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kU8String: {
        const std::string& str = std::get<std::string>(entry.str_);
        StringRef item = GetOrCreateU8String(str);
        current_func_writer_->WriteConstantEntry(item);
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kU16String: {
        const std::u16string& str = std::get<std::u16string>(entry.str_);
        StringRef item = GetOrCreateU16String(str);
        current_func_writer_->WriteConstantEntry(item);
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kBigInt: {
        BigIntRef item = GetOrCreateBigInt(entry.bigint_);
        current_func_writer_->WriteConstantEntry(item);
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kFunction: {
        auto* func = reinterpret_cast<Function*>(entry.ref_);
        FunctionRef item(
            func->func_id(),
            func->internal_formal_parameter_count_without_receiver());
        current_func_writer_->WriteConstantEntry(item);
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kScope: {
        auto* scope = reinterpret_cast<Scope*>(entry.ref_);
        ScopeRef item = GetOrCreateScope(*scope);
        current_func_writer_->WriteConstantEntry(item);
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kTopDeclarations: {
        const std::vector<String>& declarations =
            *reinterpret_cast<std::vector<String>*>(entry.ref_);
        top_declarations_.reserve(declarations.size());
        for (const String& str : declarations) {
          StringRef item = GetOrCreateString(str);
          top_declarations_.push_back(item);
        }
        current_func_writer_->WriteConstantEntry(
            UniqueScalar::TopDeclarations());
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kJumpTableSmi: {
        SmiScalar item(entry.GetJumpTableSmi());
        current_func_writer_->WriteConstantEntry(item);
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kTheHoleValue: {
        current_func_writer_->WriteConstantEntry(
            UniqueScalar(UniqueTag::kTagTheHole));
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kAsyncIteratorSymbol: {
        P_CHECK(false);  // TODO
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kClassFieldsSymbol: {
        current_func_writer_->WriteConstantEntry(
            UniqueScalar(UniqueTag::kClassFieldsSymbol));
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::
          kEmptyObjectBoilerplateDescription: {
        current_func_writer_->WriteConstantEntry(
            UniqueScalar::EmptyObjectBoilerplateDescription());
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::
          kEmptyArrayBoilerplateDescription: {
        P_CHECK(false);  // TODO
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kEmptyFixedArray: {
        P_CHECK(false);  // TODO
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kIteratorSymbol: {
        P_CHECK(false);  // TODO
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kInterpreterTrampolineSymbol: {
        P_CHECK(false);  // TODO
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kNaN: {
        current_func_writer_->WriteConstantEntry(UniqueScalar::NaN());
        break;
      }
      case ConstantArrayBuilder::Entry::Tag::kDeferred: {
        P_CHECK(false);  // impossible
        break;
      }
      default: {
        P_CHECK(false);  // TODO
        break;
      }
    }
  }
  uint32_t pos_after = current_func_writer_->Position();
  return pos_after - pos_before;
}

uint32_t PbcSerializer::SerializeTopDeclarations() {
  P_CHECK(top_declarations_.size() * sizeof(StringRef) <=
          static_cast<size_t>(UINT32_MAX));
  const uint32_t size_in_byte =
      static_cast<uint32_t>(top_declarations_.size() * sizeof(StringRef));
  script_writer_->WriteBytes(
      reinterpret_cast<const uint8_t*>(top_declarations_.data()), size_in_byte);
  return size_in_byte;
}

uint32_t PbcSerializer::SerializeDoubleTable() {
  static_assert(sizeof(double) == 8);
  P_CHECK(double_table_.size() * sizeof(double) <=
          static_cast<size_t>(UINT32_MAX));
  const uint32_t size_in_byte =
      static_cast<uint32_t>(double_table_.size() * sizeof(double));
  script_writer_->WriteBytes(
      reinterpret_cast<const uint8_t*>(double_table_.data()), size_in_byte);
  return size_in_byte;
}

uint32_t PbcSerializer::SerializeStringStorage() {
  uint32_t size_in_byte = 0;
  for (size_t i = 0; i < string_table_.size(); ++i) {
    const std::string& str = string_table_[i];
    script_writer_->WriteString(str);
    size_in_byte += str.size();
  }
  size_in_byte += AddPaddings(kAlignU16String, *script_writer_);
  return size_in_byte;
}

uint32_t PbcSerializer::SerializeU16StringStorage() {
  uint32_t size_in_byte = 0;
  for (size_t i = 0; i < u16string_table_.size(); ++i) {
    const std::u16string& str = u16string_table_[i];
    script_writer_->WriteU16String(str);
    size_in_byte += (str.size() * sizeof(char16_t));
    size_in_byte += AddPaddings(kAlignU16String, *script_writer_);
  }
  return size_in_byte;
}

uint32_t PbcSerializer::SerializeBigIntStorage() {
  uint32_t size_in_byte = 0;
  for (size_t i = 0; i < bigint_table_.size(); ++i) {
    const std::string* bigint = bigint_table_[i];
    script_writer_->WriteString(*bigint);
    size_in_byte += bigint->size();
  }
  return size_in_byte;
}

uint32_t PbcSerializer::SerializeLiteralStorage() {
  uint32_t size_in_byte = 0;
  for (size_t i = 0; i < literal_table_.size(); ++i) {
    uint8_t* ptr = literal_table_[i].first;
    uint32_t literal_size = literal_table_[i].second;
    script_writer_->WriteBytes(ptr, literal_size);
    size_in_byte += literal_size;
  }
  return size_in_byte;
}

uint32_t PbcSerializer::SerializeScopeStorage() {
  uint32_t size_in_byte = 0;
  P_CHECK(scope_table_.size() == local_var_table_.size());
  // all raw scope
  for (size_t i = 0; i < scope_table_.size(); ++i) {
    uint8_t* ptr = reinterpret_cast<uint8_t*>(scope_table_[i]);
    uint32_t scope_size = sizeof(RawScope);
    script_writer_->WriteBytes(ptr, scope_size);
    size_in_byte += scope_size;
  }
  // all local vars
  for (size_t i = 0; i < local_var_table_.size(); ++i) {
    uint8_t* ptr = local_var_table_[i].data();
    uint32_t local_size = static_cast<uint32_t>(local_var_table_[i].size());
    script_writer_->WriteBytes(ptr, local_size);
    size_in_byte += local_size;
  }
  return size_in_byte;
}

PbcSerializer::~PbcSerializer() {
  delete script_writer_;
  for (auto* writer : func_writers_) {
    delete writer;
  }
  for (auto& literal_pair : literal_table_) {
    free(literal_pair.first);
  }
  for (RawScope* raw_scope : scope_table_) {
    free(raw_scope);
  }
}

}  // namespace portable